package com.aerozhonghuan.archive.service.impl;

import com.aerozhonghuan.archive.config.DynamicDataSourceHolder;
import com.aerozhonghuan.archive.entity.CommonConstant;
import com.aerozhonghuan.archive.entity.MigrationConfig;
import com.aerozhonghuan.archive.mapper.ArchiveMapper;
import com.aerozhonghuan.archive.mapper.ArchiveMigrationConfigMapper;
import com.aerozhonghuan.archive.mapper.SourceMapper;
import com.aerozhonghuan.archive.mapper.SourceTableMetaMapper;
import com.aerozhonghuan.archive.service.EmergencyRollbackService;
import com.aerozhonghuan.archive.service.MailService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Service
public class EmergencyRollbackServiceImpl implements EmergencyRollbackService {

    private static final Logger logger = LoggerFactory.getLogger(EmergencyRollbackServiceImpl.class);

    private final ArchiveMigrationConfigMapper migrationConfigMapper;
    private final ArchiveMapper archiveMapper;
    private final SourceMapper sourceMapper;
    private final SourceTableMetaMapper tableMetaMapper;
    private final MailService mailService;

    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public EmergencyRollbackServiceImpl(ArchiveMigrationConfigMapper migrationConfigMapper,
                                        ArchiveMapper archiveMapper,
                                        SourceMapper sourceMapper,
                                        SourceTableMetaMapper tableMetaMapper,
                                        MailService mailService) {
        this.migrationConfigMapper = migrationConfigMapper;
        this.archiveMapper = archiveMapper;
        this.sourceMapper = sourceMapper;
        this.tableMetaMapper = tableMetaMapper;
        this.mailService = mailService;
    }

    /**
     * 根据指定配置ID执行紧急回迁操作，将归档库中的数据按时间范围回迁至源库，
     * 并在回迁完成后对数据一致性进行验证，通过邮件通知管理员验证结果。
     *
     * 改动说明：
     * 1. 对于每个日期分片，先调用 archiveMapper.fetchIdsByTimeRange 方法，查询该时间区间内的所有记录ID；
     * 2. 然后将 ID 列表按照 batchSize 分批，再通过 archiveMapper.fetchFullDataByIds 查询完整数据并插入到源库。
     *
     * @param configId  迁移配置记录ID
     * @param startTime 回迁起始时间
     * @param endTime   回迁结束时间
     * @param batchSize 每批次处理的记录数
     * @return 回迁操作汇总信息
     */
    @Override
    public String executeEmergencyRollbackByConfigId(Long configId, Date startTime, Date endTime, int batchSize) {
        StringBuilder summary = new StringBuilder();

        // 根据配置ID获取迁移配置记录
        MigrationConfig config = migrationConfigMapper.getById(configId);
        if (config == null) {
            String msg = "未找到配置ID为 " + configId + " 的迁移配置记录。";
            logger.warn(msg);
            return msg;
        }

        summary.append("开始紧急回迁配置记录 [归档库: ")
                .append(config.getTargetDbName()).append(".").append(config.getTargetTableName())
                .append(" -> 源库: ").append(config.getSourceDbName()).append(".").append(config.getSourceTableName())
                .append("]\n");
        logger.info("开始执行紧急回迁配置记录: {}", config);

        if (!startTime.before(endTime)) {
            String err = "紧急回迁：开始时间必须小于结束时间。";
            logger.error(err);
            return err;
        }

        // 设置目标数据源（归档库），获取归档库目标表的字段列表
        DynamicDataSourceHolder.setDynamicDataSourceKey(config.getTargetDatasource());
        List<String> columnList = tableMetaMapper.getAllColumnNames(config.getTargetDbName(), config.getTargetTableName());
        if (columnList == null || columnList.isEmpty()) {
            String err = "未获取到表 " + config.getTargetTableName() + " 的字段列表";
            logger.error(err);
            return err;
        }
        String columns = String.join(", ", columnList);
        logger.info("紧急回迁使用字段列表: {}", columns);

        // 初始化当前开始时间、累计记录数和最后恢复的时间
        Date currentStart = startTime;
        int totalRestoredRecords = 0;
        Date finalUpdateTime = currentStart;
        long oneDayMillis = 24 * 3600 * 1000L;

        // 按天分片回迁
        while (currentStart.before(endTime)) {
            // 当前天的结束时间: 当前开始时间+1天 与 endTime 取较小值
            Date currentEnd = new Date(Math.min(currentStart.getTime() + oneDayMillis, endTime.getTime()));
            logger.info("紧急回迁处理时间区间: {} ~ {}", sdf.format(currentStart), sdf.format(currentEnd));

            int restoredInSegment = 0;
            // 先查询该时间区间内所有待回迁记录的ID
            DynamicDataSourceHolder.setDynamicDataSourceKey(config.getTargetDatasource());
            List<Long> idList = archiveMapper.fetchIdsByTimeRange(
                    config.getTargetDbName(),
                    config.getTargetTableName(),
                    config.getTimeStamp(),
                    currentStart,
                    currentEnd
            );
            if (idList == null || idList.isEmpty()) {
                logger.info("归档库 [{}.{}] 时间区间 {} ~ {} 无待回迁记录。",
                        config.getTargetDbName(), config.getTargetTableName(), sdf.format(currentStart), sdf.format(currentEnd));
            } else {
                logger.info("归档库 [{}.{}] 时间区间 {} ~ {} 查询到 {} 条待回迁记录ID。",
                        config.getTargetDbName(), config.getTargetTableName(), sdf.format(currentStart), sdf.format(currentEnd), idList.size());
                // 根据 batchSize 分批处理
                for (int i = 0; i < idList.size(); i += batchSize) {
                    int endIndex = Math.min(i + batchSize, idList.size());
                    List<Long> subList = idList.subList(i, endIndex);
                    // 根据ID列表查询完整数据记录
                    DynamicDataSourceHolder.setDynamicDataSourceKey(config.getTargetDatasource());

                    List<Map<String, Object>> records = archiveMapper.fetchFullDataByIds(
                            config.getTargetDbName(),
                            config.getTargetTableName(),
                            config.getTimeStamp(),
                            subList
                    );
                    if (records == null || records.isEmpty()) {
                        continue;
                    }
                    List<Object[]> dataList = new ArrayList<>();
                    for (Map<String, Object> record : records) {
                        Object[] values = new Object[columnList.size()];
                        for (int j = 0; j < columnList.size(); j++) {
                            values[j] = record.get(columnList.get(j));
                        }
                        dataList.add(values);
                    }
                    // 切换到源数据源，执行数据回迁（插入操作）
                    DynamicDataSourceHolder.setDynamicDataSourceKey(config.getSourceDatasource());
                    try {
                        int inserted = sourceMapper.insertBatchGeneric(
                                dataList,
                                config.getSourceDbName(),
                                config.getSourceTableName(),
                                columns);
                        restoredInSegment += inserted;
                        totalRestoredRecords += inserted;
                        logger.info("紧急回迁ID范围 [{} - {}] 批次恢复: 插入 {} 条记录",
                                subList.get(0), subList.get(subList.size() - 1), inserted);
                    } catch (Exception e) {
                        String errorMsg = "紧急回迁ID范围 [" + subList.get(0) + " - " + subList.get(subList.size() - 1)
                                + "] 批次恢复失败: " + e.getMessage();
                        logger.error(errorMsg, e);
                        summary.append(errorMsg).append("\n");
                        break;
                    }
                }
            }
            if (restoredInSegment > 0) {
                finalUpdateTime = currentEnd;
            }
            // 推进到下一个天的分片
            currentStart = currentEnd;
        }
        summary.append(String.format("配置记录 [%s.%s -> %s.%s] 紧急回迁累计恢复 %d 条记录，最终恢复时间: %s\n\n",
                config.getTargetDbName(), config.getTargetTableName(),
                config.getSourceDbName(), config.getSourceTableName(),
                totalRestoredRecords, sdf.format(finalUpdateTime)));
        logger.info("紧急回迁完成，结果:\n{}", summary.toString());

        try {
            mailService.sendEmail("紧急回迁完成", summary.toString());
        } catch (Exception e) {
            logger.error("紧急回迁邮件通知发送失败", e);
        }
        DynamicDataSourceHolder.setDynamicDataSourceKey(config.getTargetDatasource());

        // 验证回迁后数据一致性
        verifyConsistencyAfterRollback(config.getId(), startTime, endTime);

        // 返回摘要信息（包含成功与错误信息），供接口调用时显示
        return summary.toString();
    }

    /**
     * 紧急回迁完成后，通过行数比对验证数据一致性，并将结果通过邮件通知管理员
     *
     * @param configId  迁移配置记录ID
     * @param startTime 回迁数据统计起始时间
     * @param endTime   回迁数据统计结束时间
     * @return 一致性验证结果摘要字符串
     */
    @Override
    public String verifyConsistencyAfterRollback(Long configId, Date startTime, Date endTime) {
        StringBuilder summary = new StringBuilder();
        // 根据配置ID获取迁移配置记录
        MigrationConfig config = migrationConfigMapper.getById(configId);
        if (config == null) {
            String msg = "未找到配置ID为 " + configId + " 的迁移配置记录。";
            logger.error(msg);
            return msg;
        }

        summary.append("开始验证紧急回迁后的数据一致性：\n")
                .append("配置记录 [归档库: ").append(config.getTargetDbName()).append(".").append(config.getTargetTableName())
                .append(" -> 源库: ").append(config.getSourceDbName()).append(".").append(config.getSourceTableName()).append("]\n");
        DynamicDataSourceHolder.setDynamicDataSourceKey(config.getTargetDatasource());

        long archiveCount = archiveMapper.countByTimeRange(
                config.getTargetDbName(),
                config.getTargetTableName(),
                config.getTimeStamp(),
                startTime, endTime);

        DynamicDataSourceHolder.setDynamicDataSourceKey(config.getSourceDatasource());

        long sourceCount = sourceMapper.countByTimeRange(
                config.getSourceDbName(),
                config.getSourceTableName(),
                config.getTimeStamp(),
                startTime, endTime);

        String formattedStart = sdf.format(startTime);
        String formattedEnd = sdf.format(endTime);
        String resultMessage = "紧急回迁时间区间 [" + formattedStart + " ~ " + formattedEnd + "]: 源库记录数 = " + sourceCount +
                "，归档库记录数 = " + archiveCount + ".";
        if (sourceCount == archiveCount) {
            resultMessage += " 数据一致。";
        } else {
            resultMessage += " 数据不一致！";
        }
        summary.append(resultMessage).append("\n");
        logger.info(resultMessage);

        try {
            mailService.sendEmail("紧急回迁数据一致性验证结果", summary.toString());
        } catch (Exception e) {
            logger.error("紧急回迁数据一致性验证邮件通知发送失败", e);
        }

        return summary.toString();
    }
}
